
import 'dart:ui' as ui;

import 'package:flutter/material.dart';
import 'package:image_pixels/image_pixels.dart';

class NinePatchImageCustom extends StatefulWidget {
  ///hide lines  in 9 patch image
  final bool hideLines;
  final Rect? customCenterSlice;
  final bool isNinePatch;
  //资源是几倍图
  final double scale;
  //像素尺寸
  final double localPixelScale;

  final ImageProvider imageProvider;

  final Widget? child;

  const NinePatchImageCustom({
    super.key,
    required this.imageProvider,
    this.child,
    this.hideLines = true,
    this.scale = 1.0,
    this.localPixelScale = 1.0,
    this.customCenterSlice,
    this.isNinePatch = true,
  });

  @override
  State<NinePatchImageCustom> createState() => _NinePatchImageCustomState();
}

class _NinePatchImageCustomState extends State<NinePatchImageCustom> {

  late NinePatchImagePainter imagePainter;

  @override
  void initState() {
    super.initState();
    imagePainter = NinePatchImagePainter(updateCallback: (){
      if(mounted){
        setState(() {
        });
      }
    });
  }


  @override
  Widget build(BuildContext context) {
    return ImagePixels(
        imageProvider: widget.imageProvider,
        builder: (context, img) {
          if (!img.hasImage) return const SizedBox.shrink();

          var leftTop = -1;
          var leftBottom = -1;

          var rightTop = -1;
          var rightBottom = -1;

          if(widget.customCenterSlice == null){
            if (img.height != null) {
              for (var i = 0; i < img.height!; i++) {
                var color = img.pixelColorAt!(0, i);
                if (color == Colors.black) {
                  if (leftTop == -1) {
                    leftTop = i;
                  }
                  if (leftTop != -1) {
                    leftBottom = i;
                  }
                }
              }

              for (var i = 0; i < img.width!; i++) {
                var color = img.pixelColorAt!(i, 0);
                if (color == Colors.black) {
                  if (rightTop == -1) {
                    rightTop = i;
                  }
                  if (rightTop != -1) {
                    rightBottom = i;
                  }
                }
              }
            } else {
              leftTop = 0;
              leftBottom = 0;
              rightTop = 0;
              rightBottom = 0;
            }
          }

          final centerSlice = widget.customCenterSlice ?? Rect.fromLTRB(
              rightTop.toDouble(),
              leftTop.toDouble(),
              rightBottom.toDouble(),
              leftBottom.toDouble()
          );

          imagePainter.updateInfo(
              img.uiImage,
              centerSlice,
              widget.isNinePatch,
              widget.scale, widget.localPixelScale);

          return ClipPath(
              clipper: BlackLineClipper(hideLines: widget.hideLines),
              child: CustomPaint(
                painter: imagePainter,
                child: widget.child,
              )
          );
        });
  }

}




class BlackLineClipper extends CustomClipper<Path> {
  final bool hideLines;

  BlackLineClipper({required this.hideLines});

  @override
  Path getClip(Size size) {
    final path = Path();
    double x = hideLines ? 2 : 0;
    path.moveTo(x, x);
    path.lineTo(x, size.height - x);
    path.lineTo(size.width - x, size.height - x);
    path.lineTo(size.width - x, x);
    path.close();
    return path;
  }

  @override
  bool shouldReclip(BlackLineClipper oldClipper) => true;
}



class NinePatchImagePainter extends CustomPainter {

  ui.Image? image;
  Rect? centerSlice;
  bool isNinePatch = false;
  double scale = 1.0;
  double localPixelScale = 1.0;

  Function() updateCallback;

  ui.Image? lastImage;
  Rect? lastCenterSlice;
  Rect? lastLogicPixelOutputRect;

  bool isCreatingTmpImage = false;
  ui.Image? imageTmp;

  NinePatchImagePainter({required this.updateCallback});

  void updateInfo(ui.Image? image, Rect centerSlice, bool isNinePatch, double scale,  double localPixelScale){
    this.image = image;
    this.centerSlice = centerSlice;
    this.isNinePatch = isNinePatch;
    this.scale = scale;
    this.localPixelScale = localPixelScale;
  }


  @override
  Future<void> paint(Canvas canvas, Size size) async {
    var outputRect = Offset.zero & size;
    if(image != null){
      final Paint paint = Paint()..isAntiAlias = false;
      paint.color = const Color.fromRGBO(0, 0, 0, 1.0);
      paint.filterQuality = FilterQuality.medium;
      paint.invertColors = false;
      paint.blendMode = BlendMode.srcOver;

      if(localPixelScale == 1.0 && scale == 1.0){
        canvas.drawImageNine(image!, centerSlice!, outputRect, paint);
      } else {
        int line = isNinePatch ? 0 : 2;
        Rect localPixelInputRect = Offset.zero & Size((image!.width - line) / scale, (image!.height - line) / scale);
        // Rect localCenterSliceRect =  Offset.zero & Size(centerSlice!.width / scale, centerSlice!.height / scale);
        Rect localPixelOutputRect = _scaleRect(outputRect, 1 / localPixelScale);

        if(localPixelInputRect == localPixelOutputRect){
          canvas.drawImageRect(image!, Offset.zero & Size(image!.width.toDouble(), image!.height.toDouble()), outputRect, paint);
        } else {
          createImageForLogic(localPixelOutputRect);
          if(imageTmp != null){
            canvas.drawImageRect(imageTmp!, Offset.zero & Size(imageTmp!.width.toDouble(), imageTmp!.height.toDouble()), outputRect, paint);
          }
        }
      }
    }
  }

  void tmpImageReset(){
    isCreatingTmpImage = false;
    imageTmp = null;
  }

  void createImageForLogic(Rect logicPixelOutputRect) async {

    Rect currentTmpRect = _scaleRect(logicPixelOutputRect, scale);
    if(lastLogicPixelOutputRect != null && (lastLogicPixelOutputRect!.width != currentTmpRect.width ||  lastLogicPixelOutputRect!.height != currentTmpRect.height)){
      tmpImageReset();
    }

    if(lastImage != image || lastCenterSlice != centerSlice) {
      tmpImageReset();
    }

    if(imageTmp == null && isCreatingTmpImage == false){
      isCreatingTmpImage = true;
      final Paint paint = Paint()..isAntiAlias = false;
      paint.color = const Color.fromRGBO(0, 0, 0, 1.0);
      paint.filterQuality = FilterQuality.medium;
      paint.invertColors = false;
      paint.blendMode = BlendMode.srcOver;
      ui.PictureRecorder recorder = ui.PictureRecorder();
      Canvas canvasTmp = Canvas(recorder);
      canvasTmp.drawImageNine(image!, centerSlice!, currentTmpRect, paint);
      ui.Picture picture = recorder.endRecording();
      imageTmp = await picture.toImage(currentTmpRect.width.toInt(), currentTmpRect.height.toInt());

      lastImage = image;
      lastLogicPixelOutputRect = currentTmpRect;
      lastCenterSlice = centerSlice;
      updateCallback.call();
    }
  }


  @override
  bool shouldRepaint(CustomPainter oldDelegate) => false;
}

Rect _scaleRect(Rect rect, double scale) => Rect.fromLTRB(rect.left * scale, rect.top * scale, rect.right * scale, rect.bottom * scale);
